home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / notification.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-03-28  |  12.8 KB  |  551 lines

  1.  
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6.  
  7. #include "WUtil.h"
  8.  
  9.  
  10. typedef struct W_Notification {
  11.     char *name;
  12.     void *object;
  13.     void *clientData;
  14.     int refCount;
  15. } Notification;
  16.  
  17.  
  18. extern void W_FlushASAPNotificationQueue();
  19.  
  20.  
  21. char*
  22. WMGetNotificationName(WMNotification *notification)
  23. {
  24.     return notification->name;
  25. }
  26.  
  27.  
  28. void*
  29. WMGetNotificationObject(WMNotification *notification)
  30. {
  31.     return notification->object;
  32. }
  33.  
  34.  
  35. void*
  36. WMGetNotificationClientData(WMNotification *notification)
  37. {
  38.     return notification->clientData;
  39. }
  40.  
  41.  
  42. WMNotification*
  43. WMCreateNotification(char *name, void *object, void *clientData)
  44. {
  45.     Notification *nPtr;
  46.     
  47.     nPtr = wmalloc(sizeof(Notification));
  48.     
  49.     nPtr->name = name;
  50.     nPtr->object = object;
  51.     nPtr->clientData = clientData;
  52.     
  53.     nPtr->refCount = 1;
  54.     
  55.     return nPtr;
  56. }
  57.  
  58.  
  59. void
  60. WMReleaseNotification(WMNotification *notification)
  61. {
  62.     notification->refCount--;
  63.     
  64.     if (notification->refCount < 1) {
  65.     wfree(notification);
  66.     }
  67. }
  68.  
  69.  
  70. WMNotification*
  71. WMRetainNotification(WMNotification *notification)
  72. {
  73.     notification->refCount++;
  74.     
  75.     return notification;
  76. }
  77.  
  78.  
  79. /***************** Notification Center *****************/
  80.  
  81. typedef struct NotificationObserver {
  82.     WMNotificationObserverAction *observerAction;
  83.     void *observer;
  84.  
  85.     char *name;
  86.     void *object;
  87.  
  88.     struct NotificationObserver *prev; /* for tables */
  89.     struct NotificationObserver *next;
  90.     struct NotificationObserver *nextAction;   /* for observerTable */
  91. } NotificationObserver;
  92.  
  93.  
  94. typedef struct W_NotificationCenter {
  95.     WMHashTable *nameTable;           /* names -> observer lists */
  96.     WMHashTable *objectTable;           /* object -> observer lists */
  97.     NotificationObserver *nilList;     /* obervers that catch everything */
  98.     
  99.     WMHashTable *observerTable;           /* observer -> NotificationObserver */
  100. } NotificationCenter;
  101.  
  102.  
  103. /* default (and only) center */
  104. static NotificationCenter *notificationCenter = NULL;
  105.  
  106.  
  107. void
  108. W_InitNotificationCenter(void)
  109. {
  110.     notificationCenter = wmalloc(sizeof(NotificationCenter));
  111.     
  112.     notificationCenter->nameTable = WMCreateHashTable(WMStringPointerHashCallbacks);
  113.     notificationCenter->objectTable = WMCreateHashTable(WMIntHashCallbacks);
  114.     notificationCenter->nilList = NULL;
  115.     
  116.     notificationCenter->observerTable = WMCreateHashTable(WMIntHashCallbacks);
  117. }
  118.  
  119.  
  120. void
  121. WMAddNotificationObserver(WMNotificationObserverAction *observerAction,
  122.               void *observer, char *name, void *object)
  123. {
  124.     NotificationObserver *oRec, *rec;
  125.     
  126.     oRec = wmalloc(sizeof(NotificationObserver));
  127.     oRec->observerAction = observerAction;
  128.     oRec->observer = observer;
  129.     oRec->name = name;
  130.     oRec->object = object;
  131.     oRec->next = NULL;
  132.     oRec->prev = NULL;
  133.     
  134.     
  135.     /* put this action in the list of actions for this observer */
  136.     rec = WMHashInsert(notificationCenter->observerTable, observer, oRec);
  137.     
  138.     if (rec) {
  139.     /* if this is not the first action for the observer */
  140.     oRec->nextAction = rec;
  141.     } else {
  142.     oRec->nextAction = NULL;
  143.     }
  144.  
  145.     if (!name && !object) {
  146.     /* catch-all */
  147.     oRec->next = notificationCenter->nilList;
  148.     if (notificationCenter->nilList) {
  149.         notificationCenter->nilList->prev = oRec;
  150.     }
  151.     notificationCenter->nilList = oRec;
  152.     } else if (!name) {
  153.     /* any message coming from object */
  154.     rec = WMHashInsert(notificationCenter->objectTable, object, oRec);
  155.     oRec->next = rec;
  156.     if (rec) {
  157.         rec->prev = oRec;
  158.     }
  159.     } else {
  160.     /* name && (object || !object) */
  161.     rec = WMHashInsert(notificationCenter->nameTable, name, oRec);
  162.     oRec->next = rec;
  163.     if (rec) {
  164.         rec->prev = oRec;
  165.     }
  166.     }
  167. }
  168.  
  169.  
  170. void
  171. WMPostNotification(WMNotification *notification)
  172. {
  173.     NotificationObserver *orec, *tmp;
  174.  
  175.     WMRetainNotification(notification);
  176.     
  177.     /* tell the observers that want to know about a particular message */
  178.     orec = WMHashGet(notificationCenter->nameTable, notification->name);
  179.     
  180.     while (orec) {
  181.     tmp = orec->next;
  182.     
  183.     if (!orec->object || !notification->object 
  184.             || orec->object == notification->object) {
  185.         /* tell the observer */
  186.         if (orec->observerAction) {
  187.         (*orec->observerAction)(orec->observer, notification);
  188.         }
  189.     }
  190.  
  191.     orec = tmp;
  192.     }
  193.  
  194.     /* tell the observers that want to know about an object */
  195.     orec = WMHashGet(notificationCenter->objectTable, notification->object);
  196.     
  197.     while (orec) {
  198.     tmp = orec->next;
  199.     
  200.     /* tell the observer */
  201.     if (orec->observerAction) {
  202.         (*orec->observerAction)(orec->observer, notification);
  203.     }
  204.     orec = tmp;
  205.     }
  206.  
  207.     /* tell the catch all observers */
  208.     orec = notificationCenter->nilList;
  209.     while (orec) {
  210.     tmp = orec->next;
  211.  
  212.     /* tell the observer */
  213.     if (orec->observerAction) {
  214.         (*orec->observerAction)(orec->observer, notification);
  215.     }
  216.     orec = tmp;
  217.     }
  218.  
  219.     WMReleaseNotification(notification);
  220. }
  221.  
  222.  
  223. void
  224. WMRemoveNotificationObserver(void *observer)
  225. {
  226.     NotificationObserver *orec, *tmp, *rec;
  227.  
  228.     /* get the list of actions the observer is doing */
  229.     orec = WMHashGet(notificationCenter->observerTable, observer);
  230.  
  231.     /*
  232.      * FOREACH orec IN actionlist for observer
  233.      * DO
  234.      *   remove from respective lists/tables
  235.      *   free
  236.      * END
  237.      */
  238.     while (orec) {
  239.     tmp = orec->nextAction;
  240.  
  241.     if (!orec->name && !orec->object) {
  242.         /* catch-all */
  243.         if (notificationCenter->nilList==orec)
  244.         notificationCenter->nilList = orec->next;
  245.     } else if (!orec->name) {
  246.         /* any message coming from object */
  247.         rec = WMHashGet(notificationCenter->objectTable, orec->object);
  248.         if (rec==orec) {
  249.         /* replace table entry */
  250.         if (orec->next) {
  251.             WMHashInsert(notificationCenter->objectTable, orec->object,
  252.                  orec->next);
  253.         } else {
  254.             WMHashRemove(notificationCenter->objectTable, orec->object);
  255.         }
  256.         }
  257.     } else {
  258.         /* name && (object || !object) */
  259.         rec = WMHashGet(notificationCenter->nameTable, orec->name);
  260.         if (rec==orec) {
  261.         /* replace table entry */
  262.         if (orec->next) {
  263.             WMHashInsert(notificationCenter->nameTable, orec->name,
  264.                  orec->next);
  265.         } else {
  266.             WMHashRemove(notificationCenter->nameTable, orec->name);
  267.         }
  268.         }
  269.     }
  270.     if (orec->prev)
  271.         orec->prev->next = orec->next;
  272.     if (orec->next)
  273.         orec->next->prev = orec->prev;
  274.  
  275.     wfree(orec);
  276.     
  277.     orec = tmp;
  278.     }
  279.  
  280.     WMHashRemove(notificationCenter->observerTable, observer);
  281. }
  282.  
  283.  
  284. void
  285. WMRemoveNotificationObserverWithName(void *observer, char *name, void *object)
  286. {
  287.     NotificationObserver *orec, *tmp, *rec;
  288.     NotificationObserver *newList = NULL;
  289.  
  290.     /* get the list of actions the observer is doing */
  291.     orec = WMHashGet(notificationCenter->observerTable, observer);
  292.  
  293.     WMHashRemove(notificationCenter->observerTable, observer);
  294.  
  295.     /* rebuild the list of actions for the observer */
  296.  
  297.     while (orec) {
  298.     tmp = orec->nextAction;
  299.     if (orec->name == name && orec->object == object) {
  300.         if (!name && !object) {
  301.         if (notificationCenter->nilList == orec)
  302.             notificationCenter->nilList = orec->next;
  303.         } else if (!name) {
  304.         rec = WMHashGet(notificationCenter->objectTable, orec->object);
  305.         if (rec==orec) {
  306.             assert(rec->prev==NULL);
  307.             /* replace table entry */
  308.             if (orec->next) {
  309.             WMHashInsert(notificationCenter->objectTable, 
  310.                      orec->object, orec->next);
  311.             } else {
  312.             WMHashRemove(notificationCenter->objectTable, 
  313.                      orec->object);
  314.             }
  315.         }
  316.         } else {
  317.         rec = WMHashGet(notificationCenter->nameTable, orec->name);
  318.         if (rec==orec) {
  319.             assert(rec->prev==NULL);
  320.             /* replace table entry */
  321.             if (orec->next) {
  322.             WMHashInsert(notificationCenter->nameTable,
  323.                      orec->name, orec->next);
  324.             } else {
  325.             WMHashRemove(notificationCenter->nameTable, 
  326.                      orec->name);
  327.             }
  328.         }
  329.         }
  330.  
  331.         if (orec->prev)
  332.         orec->prev->next = orec->next;
  333.         if (orec->next)
  334.         orec->next->prev = orec->prev;
  335.         wfree(orec);
  336.         } else {
  337.         /* append this action in the new action list */
  338.         orec->nextAction = NULL;
  339.         if (!newList) {
  340.         newList = orec;
  341.         } else {
  342.         NotificationObserver *p;
  343.  
  344.         p = newList;
  345.         while (p->nextAction) {
  346.             p = p->nextAction;
  347.         }
  348.         p->nextAction = orec;
  349.         }
  350.     }
  351.     orec = tmp;
  352.     }
  353.  
  354.     /* reinsert the list to the table */
  355.     if (newList) {
  356.     WMHashInsert(notificationCenter->observerTable, observer, newList);
  357.     }
  358. }
  359.  
  360.  
  361. void
  362. WMPostNotificationName(char *name, void *object, void *clientData)
  363. {
  364.     WMNotification *notification;
  365.  
  366.     notification = WMCreateNotification(name, object, clientData);
  367.     
  368.     WMPostNotification(notification);
  369.  
  370.     WMReleaseNotification(notification);
  371. }
  372.  
  373.  
  374.  
  375. /**************** Notification Queues ****************/
  376.  
  377.  
  378. typedef struct W_NotificationQueue {
  379.     WMBag *asapQueue;
  380.     WMBag *idleQueue;
  381.  
  382.     struct W_NotificationQueue *next;
  383. } NotificationQueue;
  384.  
  385.  
  386. static WMNotificationQueue *notificationQueueList = NULL;
  387.  
  388. /* default queue */
  389. static WMNotificationQueue *notificationQueue = NULL;
  390.  
  391.  
  392. WMNotificationQueue*
  393. WMGetDefaultNotificationQueue(void)
  394. {
  395.     if (!notificationQueue)
  396.     notificationQueue = WMCreateNotificationQueue();
  397.  
  398.     return notificationQueue;
  399. }
  400.  
  401.  
  402. WMNotificationQueue*
  403. WMCreateNotificationQueue(void)
  404. {
  405.     NotificationQueue *queue;
  406.  
  407.     queue = wmalloc(sizeof(NotificationQueue));
  408.  
  409.     queue->asapQueue = WMCreateBag(8);
  410.     queue->idleQueue = WMCreateBag(8);
  411.     queue->next = notificationQueueList;
  412.  
  413.     notificationQueueList = queue;
  414.  
  415.     return queue;
  416. }
  417.  
  418.  
  419.  
  420. void
  421. WMEnqueueNotification(WMNotificationQueue *queue, WMNotification *notification,
  422.               WMPostingStyle postingStyle)
  423. {
  424.     WMEnqueueCoalesceNotification(queue, notification, postingStyle,
  425.                   WNCOnName|WNCOnSender);
  426. }
  427.  
  428.  
  429.  
  430. void
  431. WMDequeueNotificationMatching(WMNotificationQueue *queue, 
  432.                   WMNotification *notification, unsigned mask)
  433. {
  434.     WMBagIterator i;
  435.     WMNotification *tmp;
  436.  
  437.     if ((mask & WNCOnName) && (mask & WNCOnSender)) {
  438.     WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
  439.             if (notification->object == tmp->object &&
  440.                 strcmp(notification->name, tmp->name) == 0) {
  441.         WMRemoveFromBag(queue->asapQueue, tmp);
  442.         WMReleaseNotification(tmp);
  443.         break;
  444.         }
  445.     }
  446.     WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
  447.             if (notification->object == tmp->object &&
  448.                 strcmp(notification->name, tmp->name) == 0) {
  449.         WMRemoveFromBag(queue->idleQueue, tmp);
  450.         WMReleaseNotification(tmp);
  451.         break;
  452.         }
  453.     }
  454.     } else if (mask & WNCOnName) {
  455.     WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
  456.         if (strcmp(notification->name, tmp->name) == 0) {
  457.         WMRemoveFromBag(queue->asapQueue, tmp);
  458.         WMReleaseNotification(tmp);
  459.         break;
  460.         }
  461.     }
  462.     WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
  463.         if (strcmp(notification->name, tmp->name) == 0) {
  464.         WMRemoveFromBag(queue->idleQueue, tmp);
  465.         WMReleaseNotification(tmp);
  466.         break;
  467.         }
  468.     }
  469.     } else if (mask & WNCOnSender) {
  470.     WM_ITERATE_BAG(queue->asapQueue, tmp, i) {
  471.         if (notification->object == tmp->object) {
  472.         WMRemoveFromBag(queue->asapQueue, tmp);
  473.         WMReleaseNotification(tmp);
  474.         break;
  475.         }
  476.     }
  477.     WM_ITERATE_BAG(queue->idleQueue, tmp, i) {
  478.         if (notification->object == tmp->object) {
  479.         WMRemoveFromBag(queue->idleQueue, tmp);
  480.         WMReleaseNotification(tmp);
  481.         break;
  482.         }
  483.     }
  484.     }
  485. }
  486.  
  487.  
  488. void
  489. WMEnqueueCoalesceNotification(WMNotificationQueue *queue, 
  490.                   WMNotification *notification,
  491.                   WMPostingStyle postingStyle,
  492.                   unsigned coalesceMask)
  493. {
  494.     if (coalesceMask != WNCNone)
  495.     WMDequeueNotificationMatching(queue, notification, coalesceMask);
  496.  
  497.     switch (postingStyle) {
  498.      case WMPostNow:
  499.     WMPostNotification(notification);
  500.         WMReleaseNotification(notification);
  501.         break;
  502.  
  503.      case WMPostASAP:
  504.     WMPutInBag(queue->asapQueue, notification);
  505.     break;
  506.  
  507.      case WMPostWhenIdle:
  508.     WMPutInBag(queue->idleQueue, notification);
  509.     break;
  510.     }
  511. }
  512.  
  513.  
  514. void
  515. W_FlushASAPNotificationQueue()
  516. {
  517.     WMNotificationQueue *queue = notificationQueueList;
  518.  
  519.     while (queue) {
  520.     while (WMGetBagItemCount(queue->asapQueue)) {
  521.         WMNotification *tmp = WMGetFromBag(queue->asapQueue, 0);
  522.  
  523.         WMPostNotification(tmp);
  524.             WMReleaseNotification(tmp);
  525.             WMDeleteFromBag(queue->asapQueue, 0);
  526.     }
  527.  
  528.     queue = queue->next;
  529.     }
  530. }
  531.  
  532.  
  533. void
  534. W_FlushIdleNotificationQueue()
  535. {
  536.     WMNotificationQueue *queue = notificationQueueList;
  537.  
  538.     while (queue) {
  539.     while (WMGetBagItemCount(queue->idleQueue)) {
  540.         WMNotification *tmp = WMGetFromBag(queue->idleQueue, 0);
  541.  
  542.         WMPostNotification(tmp);
  543.             WMReleaseNotification(tmp);
  544.         WMDeleteFromBag(queue->idleQueue, 0);
  545.     }
  546.  
  547.     queue = queue->next;
  548.     }
  549. }
  550.  
  551.